// Cache的需求
// 最大容量
package lru

import "container/list"

/**/
type Cache struct {
	maxBytes  int
	curBytes  int
	cache     map[string]*list.Element
	ll        *list.List
	onEvicted func(key string, value Value)
}

type entry struct {
	key   string
	value Value
}

type Value interface {
	Len() int
}

func NewCache(maxBytes int, onEvicted func(key string, value Value)) *Cache {
	c := new(Cache)
	c.Init(maxBytes, onEvicted)
	return c
}

func (c *Cache) Init(maxBytes int, onEvicted func(key string, value Value)) *Cache {
	c.maxBytes = maxBytes
	c.onEvicted = onEvicted
	c.cache = make(map[string]*list.Element)
	c.ll = list.New()
	return c
}

func (c *Cache) Get(key string) (Value, bool) {
	if ele, ok := c.cache[key]; ok {
		c.ll.MoveToFront(ele)
		v := ele.Value.(*entry)
		return v.value, true

	} else {
		return nil, false
	}
}

func (c *Cache) Add(key string, v Value) bool {
	if oldEv, ok := c.cache[key]; ok {
		c.ll.MoveToFront(oldEv)
		oldKv := oldEv.Value.(*entry)
		oldEv.Value = &entry{key, v}
		c.curBytes += v.Len() - oldKv.value.Len()
	} else {
		c.cache[key] = c.ll.PushFront(&entry{key, v})
		c.curBytes += v.Len() + len(key)
	}
	c.evicte()
	return true
}

func (c *Cache) Len() int {
	return c.ll.Len()
}

func (c *Cache) evicte() {
	for c.curBytes > c.maxBytes && c.maxBytes != 0 {
		lnode := c.ll.Back()
		if lnode != nil {
			v := lnode.Value.(*entry)
			c.ll.Remove(lnode)
			delete(c.cache, v.key)
			c.curBytes -= v.value.Len()
			if c.onEvicted != nil {
				c.onEvicted(v.key, v.value)
			}
		}
	}
}
